#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""CSV雪票数据处理系统 - 核心数据融合模块
根据new_requirement.txt的需求，重构execute.py的核心代码，实现表格数据的融合处理
"""
import os
import re
import sys
import pandas as pd
from datetime import datetime, timedelta
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Border, Side
from collections import defaultdict

class CSVDataFusion:
    """CSV数据融合类，负责两个表格的数据处理和融合"""
    
    def __init__(self):
        # 初始化数据结构
        self.table1_data = defaultdict(dict)  # {日期：{{姓名,身份证号}:{手机号,是否为主行人,是否为跟行人,是否要被新增,是否要被删除},…}}
        self.table2_data = defaultdict(dict)  # {日期：{{姓名,身份证号}:{手机号,是否为主行人,是否为跟行人,是否要被新增,是否要被删除},…}}
        self.final_data = defaultdict(list)   # 最终融合后的数据，使用列表保持顺序
        self.table1_order = []  # 存储表格1的原始顺序
    
    def normalize_date(self, date_input):
        """将各种日期格式统一为"几月几日"格式"""
        if not date_input:
            return ""
        
        # 处理Excel序列号格式（数字或数字字符串）
        if isinstance(date_input, (int, float)):
            try:
                # Excel的日期序列号从1899-12-30开始
                excel_epoch = datetime(1899, 12, 30)
                dt = excel_epoch + timedelta(days=int(date_input))
                return f"{dt.month}月{dt.day}日"
            except (ValueError, OverflowError):
                pass  # 继续尝试其他格式
        
        # 处理字符串格式
        date_str = str(date_input).strip()
        if not date_str:
            return ""
        
        # 检查是否为数字字符串（Excel序列号）
        if date_str.isdigit():
            try:
                serial_num = int(date_str)
                excel_epoch = datetime(1899, 12, 30)
                dt = excel_epoch + timedelta(days=serial_num)
                return f"{dt.month}月{dt.day}日"
            except (ValueError, OverflowError):
                pass  # 继续尝试其他格式
        
        # 处理已格式化的中文日期
        if '月' in date_str and '日' in date_str:
            # 检查是否已经是正确的格式
            try:
                month = int(date_str.split('月')[0])
                day = int(date_str.split('月')[1].split('日')[0])
                if 1 <= month <= 12 and 1 <= day <= 31:
                    return date_str  # 已经是正确格式
            except (ValueError, IndexError):
                pass
        
        # 定义可能的日期格式
        date_formats = [
            '%Y-%m-%d',
            '%Y/%m/%d',
            '%Y.%m.%d',
            '%m-%d',
            '%m/%d',
            '%Y年%m月%d日',
            '%m月%d日',
            '%m.%d',
            '%m-%d-%Y',
            '%m/%d/%Y'
        ]
        
        # 尝试解析日期
        for fmt in date_formats:
            try:
                dt = datetime.strptime(date_str, fmt)
                return f"{dt.month}月{dt.day}日"
            except ValueError:
                continue
        
        # 如果无法解析，返回空字符串
        return ""
    
    def parse_companion_info(self, text):
        """智能解析单个同行人信息，支持所有格式
        
        支持的格式：
        1. 标准格式：姓名、手机号、身份证号用空格分隔，顺序不限
        2. 紧密排列：姓名手机号身份证号无分隔符
        3. 特殊格式：包含描述性文字和各种分隔符
        4. 任意顺序：手机号、身份证号可以在姓名前面
        
        参数:
            text: 同行人信息文本
        
        返回:
            包含姓名、身份证、手机号的字典
        """
        if not text or not text.strip():
            return None
        
        text = text.strip()
        result = {'name': '', 'id_card': '', 'phone': ''}
        
        # 步骤1: 清理描述性文字和特殊符号
        # 更强大的清理规则，处理各种特殊字符和描述性文字
        clean_text = re.sub(r'(手机号|电话|手机|身份证|号码|姓名|名字|【|】|\(|\)|（|）|"|"|\'|\'|:|：|\s|_|,|，|、|;|；|—|-|_|\u3000)+', ' ', text, flags=re.IGNORECASE)
        clean_text = re.sub(r'\s+', ' ', clean_text)
        clean_text = clean_text.strip()
        
        if not clean_text:
            return None
        
        # 步骤2: 提取所有关键信息
        parts = clean_text.split()
        
        # 识别各个部分
        for part in parts:
            part = part.strip()
            if not part:
                continue
                
            # 识别18位身份证号
            if re.fullmatch(r'\d{17}[\dXx]', part):
                result['id_card'] = part
            # 识别11位手机号（以1开头）
            elif re.fullmatch(r'1[3-9]\d{9}', part):
                result['phone'] = part
            # 识别中文姓名（2-4个汉字）
            elif re.fullmatch(r'[\u4e00-\u9fa5]{2,4}', part):
                result['name'] = part
        
        # 步骤3: 处理紧密排列（无空格分隔）
        if not any([result['name'], result['id_card'], result['phone']]) and len(parts) == 1:
            # 尝试从紧密排列中提取
            # 模式：任意顺序的姓名+手机号+身份证号
            patterns = [
                # 姓名 + 手机号 + 身份证
                r'^([\u4e00-\u9fa5]{2,4})(1[3-9]\d{9})(\d{17}[\dXx])$',
                # 姓名 + 身份证 + 手机号
                r'^([\u4e00-\u9fa5]{2,4})(\d{17}[\dXx])(1[3-9]\d{9})$',
                # 手机号 + 姓名 + 身份证
                r'^(1[3-9]\d{9})([\u4e00-\u9fa5]{2,4})(\d{17}[\dXx])$',
                # 手机号 + 身份证 + 姓名
                r'^(1[3-9]\d{9})(\d{17}[\dXx])([\u4e00-\u9fa5]{2,4})$',
                # 身份证 + 姓名 + 手机号
                r'^(\d{17}[\dXx])([\u4e00-\u9fa5]{2,4})(1[3-9]\d{9})$',
                # 身份证 + 手机号 + 姓名
                r'^(\d{17}[\dXx])(1[3-9]\d{9})([\u4e00-\u9fa5]{2,4})$',
                # 只有姓名和身份证
                r'^([\u4e00-\u9fa5]{2,4})(\d{17}[\dXx])$',
                r'^(\d{17}[\dXx])([\u4e00-\u9fa5]{2,4})$',
                # 只有姓名和手机号
                r'^([\u4e00-\u9fa5]{2,4})(1[3-9]\d{9})$',
                r'^(1[3-9]\d{9})([\u4e00-\u9fa5]{2,4})$',
            ]
            
            for pattern in patterns:
                match = re.match(pattern, clean_text)
                if match:
                    groups = match.groups()
                    # 重新分配识别到的信息
                    for group in groups:
                        if re.fullmatch(r'\d{17}[\dXx]', group):
                            result['id_card'] = group
                        elif re.fullmatch(r'1[3-9]\d{9}', group):
                            result['phone'] = group
                        elif re.fullmatch(r'[\u4e00-\u9fa5]{2,4}', group):
                            result['name'] = group
                    break
        
        # 步骤4: 验证结果有效性
        has_valid_info = any([
            result['name'] and len(result['name']) >= 2,
            result['id_card'] and len(result['id_card']) == 18,
            result['phone'] and len(result['phone']) == 11
        ])
        
        return result if has_valid_info else None
    
    def load_table1_data(self, excel_file):
        """从表格1（Excel文件）加载数据到指定的数据结构"""
        try:
            # 读取Excel文件
            wb = load_workbook(excel_file)
            ws = wb.active
            
            # 遍历所有行
            for row_idx in range(2, ws.max_row + 1):  # 假设第一行是表头
                date = ws.cell(row=row_idx, column=1).value
                name = ws.cell(row=row_idx, column=2).value
                id_card = ws.cell(row=row_idx, column=3).value
                phone = ws.cell(row=row_idx, column=4).value
                
                # 跳过空行或无效行
                if not name or not id_card:
                    continue
                
                # 格式化日期
                formatted_date = self.normalize_date(date)
                if not formatted_date:
                    continue
                
                # 构建键
                key = (str(name).strip(), str(id_card).strip())
                phone_str = str(phone).strip() if phone else ''
                
                # 记录原始顺序
                self.table1_order.append((formatted_date, key))
                
                # 读取Excel单元格的边框信息
                cell = ws.cell(row=row_idx, column=2)
                
                # 检查边框信息
                has_top_border = cell.border.top.style is not None and cell.border.top.style != 'none'
                has_bottom_border = cell.border.bottom.style is not None and cell.border.bottom.style != 'none'
                
                # 对于表格1，只有单元格有上边框时，is_main才为true
                is_main = has_top_border
                # 对于表格1，只有单元格有下边框时，is_companion才为true
                is_companion = has_bottom_border
                
                # 更新数据结构
                self.table1_data[formatted_date][key] = {
                    'phone': phone_str,
                    'is_main': is_main,
                    'is_companion': is_companion,
                    'to_add': False,
                    'to_delete': False
                }
            
            print(f"成功从表格1（{excel_file}）加载数据，共 {sum(len(v) for v in self.table1_data.values())} 条记录")
            return True
        except Exception as e:
            print(f"加载表格1数据时出错: {str(e)}")
            return False
    
    def load_table2_data(self, csv_file):
        """从表格2（CSV文件）加载数据到指定的数据结构"""
        try:
            # 读取CSV文件
            df = pd.read_csv(csv_file, encoding='utf-8')
            
            # 过滤只保留订单状态为"待发货"和"已关闭"的行
            df = df[df['订单状态'].isin(['待发货', '已关闭'])]
            
            # 初始化记录列表
            main_records = []
            companion_records = []
            
            for _, row in df.iterrows():
                # 提取商品留言
                product_comment = row['商品留言'] if '商品留言' in row and pd.notna(row['商品留言']) else ''
                order_status = row['订单状态'] if '订单状态' in row and pd.notna(row['订单状态']) else ''
                
                if not product_comment or not order_status:
                    continue
                
                # 解析商品留言
                name = ''
                phone = ''
                id_card = ''
                use_date = ''
                companion_info = ''
                
                # 提取姓名
                name_match = re.search(r'姓名[:：]([^;；]+)', product_comment)
                if name_match:
                    name = name_match.group(1).strip()
                
                # 提取电话
                phone_match = re.search(r'电话[:：]([^;；]+)', product_comment)
                if phone_match:
                    phone = phone_match.group(1).strip()
                
                # 提取身份证号
                id_card_match = re.search(r'身份证号[:：]([^;；]+)', product_comment)
                if id_card_match:
                    id_card = id_card_match.group(1).strip()
                
                # 提取使用日期
                date_match = re.search(r'使用日期[:：]([^;；]+)', product_comment)
                if date_match:
                    use_date = date_match.group(1).strip()
                
                # 提取同行人信息
                # 支持两种格式：拍多份的备注他人信息 和 同行人姓名+身份证号
                companion_match = re.search(r'(拍多份的备注他人信息|同行人姓名\+身份证号)[:：]([^;；]*)', product_comment)
                if companion_match:
                    # group(2)是实际的同行人信息内容
                    companion_info = companion_match.group(2).strip()
                
                # 格式化日期
                formatted_date = self.normalize_date(use_date)
                if not formatted_date or not name or not id_card:
                    continue
                
                # 构建键
                key = (name, id_card)
                phone_str = phone.strip() if phone else ''
                
                # 处理同行人信息
                companions = []
                if companion_info:
                    separators = r'[\n,;\s]+'
                    companion_parts = re.split(separators, companion_info)
                    companion_parts = [part.strip() for part in companion_parts if part.strip()]
                    
                    for part in companion_parts[:2]:  # 限制只处理两个同行人
                        companion_data = self.parse_companion_info(part)
                        if companion_data:
                            companions.append(companion_data)
                        else:
                            companions.append({
                                'name': part,
                                'id_card': '',
                                'phone': phone or ''
                            })
                
                # 保存主行人和同行人的关系
                main_records.append({
                    'key': key,
                    'date': formatted_date,
                    'phone': phone_str,
                    'has_companions': len(companions) > 0,
                    'order_status': order_status
                })
                
                for companion in companions:
                    if companion['name']:
                        companion_key = (companion['name'], companion.get('id_card', ''))
                        companion_records.append({
                            'key': companion_key,
                            'date': formatted_date,
                            'main_key': key,
                            'phone': companion.get('phone', phone_str),
                            'order_status': order_status
                        })
            
            # 先添加所有主行人记录
            for record in main_records:
                # 对于表格2，只有有跟行人时，is_main才为true
                is_main = record['has_companions']
                self.table2_data[record['date']][record['key']] = {
                    'phone': record['phone'],
                    'is_main': is_main,
                    'is_companion': False,
                    'to_add': record['order_status'] == '待发货',
                    'to_delete': record['order_status'] == '已关闭'
                }
            
            # 再添加所有同行人记录
            for record in companion_records:
                # 对于表格2，只有有主行人时，is_companion才为true
                is_companion = record['main_key'] in self.table2_data[record['date']]
                self.table2_data[record['date']][record['key']] = {
                    'phone': record['phone'],
                    'is_main': False,
                    'is_companion': is_companion,
                    'to_add': record['order_status'] == '待发货',
                    'to_delete': record['order_status'] == '已关闭'
                }
            
            print(f"成功从表格2（{csv_file}）加载数据，共 {sum(len(v) for v in self.table2_data.values())} 条记录")
            return True
        except Exception as e:
            print(f"加载表格2数据时出错: {str(e)}")
            return False
    
    def fuse_data(self):
        """融合表格1和表格2的数据，确保数据顺序符合要求：
        - 保留表格1中的数据顺序
        - 表格2的数据按日期新添在表格1的数据之后
        """
        try:
            # 创建一个集合来跟踪已处理的记录
            processed_records = set()
            
            # 首先按照表格1的原始顺序添加数据
            for date, key in self.table1_order:
                if key in self.table1_data[date]:
                    info = self.table1_data[date][key].copy()
                    
                    # 检查表格2是否有删除标记
                    if date in self.table2_data and key in self.table2_data[date] and self.table2_data[date][key]['to_delete']:
                        info['to_delete'] = True
                        print(f"标记删除：日期={date}, 姓名={key[0]}, 身份证={key[1]}")
                    
                    # 只有未标记删除的才添加到最终数据
                    if not info['to_delete']:
                        self.final_data[date].append((key, info))
                        
                    # 标记为已处理
                    processed_records.add((date, key))
            
            # 然后按照表格2的数据顺序添加新增的记录
            new_records = []
            for date, records in self.table2_data.items():
                for key, info in records.items():
                    # 检查是否已处理（在表格1中存在）
                    if (date, key) not in processed_records and info['to_add']:
                        # 如果表格1中不存在且是待发货状态，添加到新记录列表
                        new_records.append((date, key, info))
                        print(f"添加新记录：日期={date}, 姓名={key[0]}, 身份证={key[1]}")
            
            # 按日期分组并添加新记录
            for date, key, info in new_records:
                self.final_data[date].append((key, info))
            
            print(f"数据融合完成，最终数据共 {sum(len(v) for v in self.final_data.values())} 条记录")
            return True
        except Exception as e:
            print(f"融合数据时出错: {str(e)}")
            return False
    
    def apply_border(self, ws, row_num, column_num, is_main=False, is_companion=False):
        """根据is_main和is_companion状态应用边框样式
        
        只在is_main为true时，才绘制上边框与左右边框
        只在is_companion为true时，才绘制下边框与左右边框
        否则一律都不绘制边框
        """
        cell = ws.cell(row=row_num, column=column_num)
        
        # 初始化边框样式
        left = None
        right = None
        top = None
        bottom = None
        
        if is_main:
            # 绘制上边框与左右边框
            left = Side(style='thin')
            right = Side(style='thin')
            top = Side(style='thin')
            # 下边框取决于是否也是同行人
            bottom = Side(style='thin') if is_companion else Side(style=None)
        elif is_companion:
            # 绘制下边框与左右边框
            left = Side(style='thin')
            right = Side(style='thin')
            top = Side(style=None)
            bottom = Side(style='thin')
        
        # 应用边框
        if any([left, right, top, bottom]):
            cell.border = Border(
                left=left or Side(style=None),
                right=right or Side(style=None),
                top=top or Side(style=None),
                bottom=bottom or Side(style=None)
            )
    
    def generate_excel(self, output_file):
        """生成最终的Excel文件"""
        try:
            # 创建新的Excel工作簿
            wb = Workbook()
            ws = wb.active
            ws.title = "雪票统计"
            
            # 写入表头
            headers = ['日期', '姓名', '身份证号', '手机号']
            for col_idx, header in enumerate(headers, 1):
                ws.cell(row=1, column=col_idx, value=header)
            
            # 按日期排序
            def sort_date_key(date_str):
                """日期排序键"""
                try:
                    if '月' in date_str and '日' in date_str:
                        month = int(date_str.split('月')[0])
                        day = int(date_str.split('月')[1].split('日')[0])
                        return (month, day)
                    return (0, 0)
                except:
                    return (0, 0)
            
            # 过滤掉空的日期组并排序
            valid_dates = [date for date in self.final_data.keys() if self.final_data[date]]
            sorted_dates = sorted(valid_dates, key=sort_date_key, reverse=True)
            
            # 写入数据
            current_row = 2
            
            for date_idx, date_str in enumerate(sorted_dates):
                # 不同日期之间插入空行（除了第一个日期）
                if date_idx > 0:
                    current_row += 1
                
                # 处理当前日期的所有记录
                for key, info in self.final_data[date_str]:
                    # 写入数据
                    ws.cell(row=current_row, column=1, value=date_str)
                    ws.cell(row=current_row, column=2, value=key[0])
                    ws.cell(row=current_row, column=3, value=key[1])
                    ws.cell(row=current_row, column=4, value=info['phone'])
                    
                    # 应用边框样式
                    self.apply_border(ws, current_row, 2, info['is_main'], info['is_companion'])
                    
                    current_row += 1
            
            # 调整列宽
            from openpyxl.utils import get_column_letter
            for col_idx in range(1, 5):
                column_letter = get_column_letter(col_idx)
                max_length = 0
                for row in ws.iter_rows(values_only=True):
                    if row and len(row) >= col_idx and row[col_idx-1] is not None:
                        max_length = max(max_length, len(str(row[col_idx-1])))
                ws.column_dimensions[column_letter].width = min(max_length + 2, 50)
            
            # 保存文件
            wb.save(output_file)
            print(f"Excel文件已保存到：{output_file}")
            return True
        except Exception as e:
            print(f"生成Excel文件时出错: {str(e)}")
            import traceback
            traceback.print_exc()
            return False

    def get_latest_csv_file(self, directory=None):
        """获取指定目录下最新的CSV文件"""
        try:
            if directory is None:
                directory = os.path.expanduser("~/Desktop")
            
            # 获取所有符合条件的CSV文件
            import glob
            csv_files = glob.glob(os.path.join(directory, "*.csv"))
            csv_files = [f for f in csv_files if f.startswith(os.path.join(directory, "Goods_youzan"))]
            
            if not csv_files:
                # 检查是否有data.csv
                default_csv = os.path.join(directory, "data.csv")
                if os.path.exists(default_csv):
                    return default_csv
                else:
                    print("警告：未找到CSV文件")
                    return None
            
            # 按创建时间排序，获取最新的文件
            csv_files.sort(key=lambda x: os.path.getctime(x), reverse=True)
            return csv_files[0]
        except Exception as e:
            print(f"获取最新CSV文件时出错: {str(e)}")
            return None

    def determine_product_type(self, csv_file):
        """根据CSV文件确定产品类型（太仓或耀雪）"""
        try:
            # 读取CSV文件的前几行来判断产品类型
            df = pd.read_csv(csv_file, encoding='utf-8', nrows=5)
            
            # 检查是否有'商品名称'列
            if '商品名称' in df.columns:
                # 查看所有非空的商品名称值
                product_names = df['商品名称'].dropna().astype(str)
                for name in product_names:
                    if '耀雪' in name:
                        return '耀雪'
                    elif '太仓' in name:
                        return '太仓'
            
            # 默认返回'太仓'
            return '太仓'
        except Exception as e:
            print(f"确定产品类型时出错: {str(e)}")
            return '太仓'

if __name__ == '__main__':
    # 创建数据融合实例
    data_fusion = CSVDataFusion()
    
    # 获取最新的CSV文件
    latest_csv = data_fusion.get_latest_csv_file()
    if not latest_csv:
        print("未找到CSV文件，程序退出")
        sys.exit(1)
    
    print(f"使用CSV文件: {os.path.basename(latest_csv)}")
    
    # 确定产品类型
    product_type = data_fusion.determine_product_type(latest_csv)
    print(f"产品类型: {product_type}")
    
    # 确定Excel文件路径
    desktop_path = os.path.expanduser("~/Desktop")
    if product_type == "太仓":
        excel_file = os.path.join(desktop_path, "太仓雪票统计.xlsx")
    elif product_type == "耀雪":
        excel_file = os.path.join(desktop_path, "耀雪雪票统计.xlsx")
    else:
        excel_file = os.path.join(desktop_path, "雪票统计.xlsx")
    
    output_file = os.path.join(desktop_path, "result.xlsx")
    
    # 加载表格1数据
    if not data_fusion.load_table1_data(excel_file):
        print("加载表格1数据失败，程序退出")
        sys.exit(1)
    
    # 加载表格2数据
    if not data_fusion.load_table2_data(latest_csv):
        print("加载表格2数据失败，程序退出")
        sys.exit(1)
    
    # 融合数据
    if not data_fusion.fuse_data():
        print("融合数据失败，程序退出")
        sys.exit(1)
    
    # 生成Excel文件
    if data_fusion.generate_excel(output_file):
        print("数据处理完成！")
    else:
        print("生成Excel文件失败！")